package sim;

/**
 * Main Simulator class for the discrete event simulation
 * 
 * @author David Stezenbach
 * @author Michael Hoefling
 *
 */
public class Simulator {
	private DES01 des;
	private long simTimeInRealTime;
	private long now;
	private SortableQueue ec;
	private boolean stop;
	private SortableQueue customers;
	
	public Simulator(DES01 des) {
		this.des = des;
		ec = new SortableQueue();
		customers = new SortableQueue();
	}
	
	/**
	 * Sets the number of units in sim time representing one unit in real time
	 * 
	 * @param simTimeInRealTime number of ticks per unit in real time
	 */
	public void setSimTimeInRealTime(long simTimeInRealTime) {
		this.simTimeInRealTime = simTimeInRealTime;
	}
	
	/**
	 * Converts real time to sim time
	 * 
	 * @param realTime units in real time
	 * @return units in sim time
	 * @throws Exception if simtime is out of range
	 */
	public long realTimeToSimTime(double realTime) throws NumberFormatException {
		double tmp = realTime * simTimeInRealTime;
		if(tmp > Long.MAX_VALUE)
			throw new NumberFormatException("simulation time out of range: " + tmp + " > " + Long.MAX_VALUE);
		return (long) Math.ceil(tmp);
	}
	
	/**
	 * Converts sim time to real time
	 * @param simTime units in sim time
	 * @return units in real time
	 */
	public double simTimeToRealTime(long simTime) {
		return (double) simTime / simTimeInRealTime;
	}
	
	/**
	 * Starts the simulation
	 * 
	 * @throws Exception is thrown when event order is invalid
	 */
	public void run() {
		while(!stop) {
			Event e = (Event) ec.popNextElement();
			if(e != null) {
				/*
				 * check if event time is in the past
				 */
				if(e.getTime() < now)
					throw new RuntimeException("Event time " + e.getTime() 
							+ " smaller than current time " + now);
				/*
				 * set event time as new simtime
				 */
				now = e.getTime();
				/*
				 * process event
				 */
				e.process();
			} else {
				System.out.println("Event chain empty.");
				stop = true;
			}
		}
	}
	
	/**
	 * Pushes a new event into the event chain at the correct place
	 * @param e the new event
	 */
	public void pushNewEvent(Event e) {
		ec.pushNewElement(e);
	}
	
	/**
	 * Stops the simulator
	 */
	public void stop() {
		stop = true;
	}
	
	public void start() {
		stop = false;
	}
	
	/**
	 * Returns the current sim time
	 * @return current sim time
	 */
	public long getSimTime() {
		return now;
	}
	
	/**
	 * Resets the simulator
	 */
	public void reset() {
		now = 0;
		stop = false;
		ec.clear();
	}
	
	public void addCustomer(Customer c){
		customers.pushNewElement(c);
	}
	
	public void removeCustomer(){
		Customer c = (Customer) customers.popNextElement();
		des.discreteCounter.graspCustomer(c);
	}
	
	public void setInitiationTime(long time){
		Customer c = (Customer) customers.popNextElement();
		c.setInitiationTime(time);
		customers.pushNewElement(c);
	}
	
	public void setCompletionTime(long time){
		Customer c = (Customer) customers.popNextElement();
		c.setCompletionTime(time);
		customers.pushNewElement(c);
		des.continousCounter.graspCustomer(c);
	}
	
}
